#ifndef ARC_CACHE_HPP
#define ARC_CACHE_HPP

#include "../CachePolicyIface.h"
#include "./ArcLruPart.hpp"
#include "./ArcLfuPart.hpp"

namespace CachePool
{
    template <typename Key, typename Value>
    class ArcCache : public CachePolicyIface<Key, Value>
    {
    public:
        explicit ArcCache(const size_t capacity = 10, const size_t transformThreshold = 2)
            : capacity_(capacity),
              transformThreshold_(transformThreshold),
              lruPart_(std::make_unique<ArcLruPart<Key, Value>>(capacity, transformThreshold)),
              lfuPart_(std::make_unique<ArcLfuPart<Key, Value>>(capacity, transformThreshold))
        {
        }

        ~ArcCache() override = default;

        void set(const Key &key, const Value &value) override
        {
            bool inGhost = this->checkGhostCaches(key);

            if (inGhost) // 刚淘汰的又访问，是最近访问，插入lru
            {
                this->lruPart_->set(key, value);
            }
            else // 两个缓存都记录
            {
                if (this->lruPart_->set(key, value))
                {
                    this->lfuPart_->set(key, value);
                }
            }
        }

        bool get(const Key &key, Value &value) override
        {
            this->checkGhostCaches(key);

            bool shouldTransform = false;
            if (this->lruPart_->get(key, value, shouldTransform))
            {
                if (shouldTransform)
                {
                    this->lfuPart_->set(key, value);
                }
                return true;
            }

            return this->lfuPart_->get(key, value);
        }

        Value get(const Key &key) override
        {
            Value value{};
            this->get(key, value);
            return value;
        }

    private:
        // 检查lru和lfu的淘汰链表，有就从链表移除
        // lru有就减小lfu增大lru，lfu有就减小lru增大lfu
        // （知道从lru淘汰的又访问了，需要自适应增大lru，反之成立）
        bool checkGhostCaches(const Key &key)
        {
            bool inGhost = false;

            if (this->lruPart_->checkGhost(key))
            {
                if (this->lfuPart_->decreaseCapacity())
                {
                    this->lruPart_->increaseCapacity();
                }

                inGhost = true;
            }
            else if (this->lfuPart_->checkGhost(key))
            {
                if (this->lruPart_->decreaseCapacity())
                {
                    this->lfuPart_->increaseCapacity();
                }

                inGhost = true;
            }

            return inGhost;
        }

    private:
        size_t capacity_;
        size_t transformThreshold_;

        std::unique_ptr<ArcLruPart<Key, Value>> lruPart_;
        std::unique_ptr<ArcLfuPart<Key, Value>> lfuPart_;
    };

} // namespace CachePool

#endif // ARC_CACHE_HPP